package com.chenju.invokechainservice.service.impl;

import com.chenju.invokechainservice.adapter.ClassNodeAdapter;
import com.chenju.invokechainservice.adapter.MethodNodeAdapter;
import com.chenju.invokechainservice.common.enums.EasyCode;
import com.chenju.invokechainservice.common.exceptions.FunnyFileNotFoundException;
import com.chenju.invokechainservice.component.BuildComponent;
import com.chenju.invokechainservice.dao.TClassNodeRepository;
import com.chenju.invokechainservice.domain.*;
import com.chenju.invokechainservice.service.*;
import com.chenju.invokechainservice.utils.FunnyFileUtils;
import com.chenju.invokechainservice.utils.FunnyListsUtils;
import com.chenju.invokechainservice.utils.FunnyStringUtils;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.util.*;

@Service
public class TClassNodeServiceImplement implements TClassNodeService {
    private final Logger logger = LoggerFactory.getLogger(TClassNodeServiceImplement.class);

    @Autowired
    private BuildComponent build;

    private final TClassNodeRepository repository;
    private final TAnnotationNodeService annotationNodeService;
    private final TMethodNodeService methodNodeService;
    private final TInterfaceNodeService interfaceNodeService;

    public TClassNodeServiceImplement(TClassNodeRepository repository, TAnnotationNodeService annotationNodeService, TMethodNodeService methodNodeService, TInterfaceNodeService interfaceNodeService) {
        this.repository = repository;
        this.annotationNodeService = annotationNodeService;
        this.methodNodeService = methodNodeService;
        this.interfaceNodeService = interfaceNodeService;
    }

    // 单文件解析
    @Override
    public void parseSingleFileToNode(ClassNodeAdapter cna, String applicationName) {
        String className = FunnyStringUtils.findDotString(cna.name);
        TClassNode classNode = new TClassNode(className);

        // 保存类的注解, 并分析类的注解如Controller等
        TClassNode parsedClassNode = (TClassNode) annotationNodeService.parseAnnotation(classNode, cna.visibleAnnotations);

        // 创建MethodNode并建立与ClassNode的联系
        List<MethodNodeAdapter> methods = cna.methods;
        List<TMethodNode> methodNodes = new ArrayList<>();
        if (!CollectionUtils.isEmpty(methods)) {
            methods.forEach(method -> {
                String methodName = className + "." + method.name;
                // 添加这个步骤主要是考虑方法中包含lambda$xxx0的样式
                // TMethodNode methodNode = Optional.ofNullable(methodNodeService.findByNameAndDesc(methodName, method.desc)).orElse(new TMethodNode(methodName, method.desc));
                TMethodNode methodNode = new TMethodNode(methodName, method.desc);
                // 解析方法的desc, 获得ReturnType和Parameter Node
                TMethodNode parsedMethodNode = methodNodeService.parseMethod(methodNode, method);
                methodNodes.add(methodNodeService.save(parsedMethodNode));
            });
        }

        // 创建类与方法的关系
        parsedClassNode.setMethods(methodNodes);
        // 设置类所属的application
        parsedClassNode.setApplicationName(applicationName);
        save(parsedClassNode);
    }

    // 多文件联合解析
    @Override
    public void parseMultipleFileToNode(ClassNodeAdapter cna, String applicationName) {
        String className = FunnyStringUtils.findDotString(cna.name);
        List<MethodNodeAdapter> methods = cna.methods;
        // 找到该Class Node
        TClassNode currentClassNode;
        List<TMethodNode> methodNodes;

        // 设置调用关系
        if (!CollectionUtils.isEmpty(methods)) {
            methods.forEach(method -> {
                TMethodNode methodNode = methodNodeService.findByNameAndDesc(className + "." + method.name, method.desc);
                TMethodNode parsedMethodNodeInvoke = methodNodeService.parseMethodInvoke(methodNode, method);
                methodNodeService.save(parsedMethodNodeInvoke);
            });
        }

        currentClassNode = findByNameAndApplicationName(className, applicationName);
        methodNodes = currentClassNode.getMethods();
        if (!CollectionUtils.isEmpty(methodNodes)) {
            for (TMethodNode methodNode : methodNodes) {
                // Lambda关系
                if (methodNode.getName().contains("lambda$")){
                    String lambdaFromMethodName = FunnyStringUtils.findRealMethodNameFromLambdaMethod(methodNode.getName());
                    methodNode.setLambdaFromMethodNode(methodNodeService.findByName(FunnyStringUtils.findPreLevelName(methodNode.getName()) + "." + lambdaFromMethodName));
                }

                // 继承关系
                if (cna.superName != null) {
                    TClassNode superClassNode = findByName(FunnyStringUtils.findDotString(cna.superName));
                    List<TMethodNode> superMethods = superClassNode.getMethods();
                    if (!CollectionUtils.isEmpty(superMethods)) {
                        for (TMethodNode superMethod : superMethods) {
                            if (FunnyStringUtils.findLastName(methodNode.getName()).equals(FunnyStringUtils.findLastName(superMethod.getName())) && methodNode.getDescriptor().equals(superMethod.getDescriptor())) {
                                // 目前通过比对名称和desc来确定两个函数一致，确定为override方法,后续考虑添加signature比较
                                methodNode.setExtendsMethodNode(superMethod);
                                methodNode.setOverride(true);
                            }
                        }
                    }
                }

                // 实现关系
                if (!CollectionUtils.isEmpty(cna.interfaces)) {
                    for (String implementClassName : cna.interfaces) {
                        TClassNode implementClassNode = findByName(FunnyStringUtils.findDotString(implementClassName));
                        List<TMethodNode> implementMethods = implementClassNode.getMethods();
                        if (!CollectionUtils.isEmpty(implementMethods)) {
                            for (TMethodNode implementMethod : implementMethods) {
                                if (FunnyStringUtils.findLastName(methodNode.getName()).equals(FunnyStringUtils.findLastName(implementMethod.getName())) && methodNode.getDescriptor().equals(implementMethod.getDescriptor())) {
                                    // 目前通过比对名称和desc来确定两个函数一致，确定为override方法,后续考虑添加signature比较
                                    methodNode.updateImplementsMethodNodes(implementMethod);
                                    methodNode.setOverride(true);
                                }
                            }
                        }
                    }
                }

                // 接口关系
                if (methodNode.isInterface() && currentClassNode.isController()) {
                    String mappingPath = currentClassNode.getMappingPath() + methodNode.getMappingPath();
                    String requestMethod = methodNode.getRequestMethod();
                    // 新建Interface Node
                    TInterfaceNode interfaceNode = interfaceNodeService.save(new TInterfaceNode(mappingPath, requestMethod));
                    methodNode.setTInterfaceNode(interfaceNode);
                }

                // 环绕关系
                if (methodNode.isAdvices()) {
                    List<TAnnotationNode> annotations = methodNode.getAnnotations();
                    Map<String, String> annotationValueMap = new HashMap<>();
                    for (TAnnotationNode annotation : annotations) {
                        if ("org.aspectj.lang.annotation.Before".equals(annotation.getName()) || "org.aspectj.lang.annotation.After".equals(annotation.getName()) || "org.aspectj.lang.annotation.AfterReturning".equals(annotation.getName()) || "org.aspectj.lang.annotation.AfterThrowing".equals(annotation.getName()) || "org.aspectj.lang.annotation.Around".equals(annotation.getName())) {
                            annotationValueMap = FunnyListsUtils.groupItems(annotation.getValues());
                        }
                    }
                    String advicedMethodName = FunnyStringUtils.findPreLevelName(methodNode.getName()) + "." + FunnyStringUtils.findLastName(annotationValueMap.get("value"));
                    // 设置注解中指明的方法
                    methodNode.setAdviceForMethod(methodNodeService.findByName(advicedMethodName));
                }

                // Feign调用关系
                if (methodNode.isInterface() && currentClassNode.isFeignClient()) {
                    // 查找FeignClient的注解参数
                    Map<String, String> feignClientAnnotationParamsMap = annotationNodeService.findAnnotationParamsMapByAnnotationClass(currentClassNode.getAnnotations(), "org.springframework.cloud.openfeign.FeignClient");
                    String applicationServerName = feignClientAnnotationParamsMap.get("value");
                    String configurationClassName = Type.getType(feignClientAnnotationParamsMap.get("configuration")).getClassName();
                    currentClassNode.updateConfigClass(findByName(configurationClassName));
                    // 查找method的mappingPath
                    String mappingPath = methodNode.getMappingPath();
                    TInterfaceNode feignInvokedInterfaceNode = interfaceNodeService.findByApplicationNameAndName(applicationServerName, mappingPath);
                    methodNode.setFeignInterface(feignInvokedInterfaceNode);
                }
                methodNodeService.save(methodNode);
            }
        }
    }

//    @Override
//    public void parseProjectToNode(String codePath) {
//        // 按分类获取File Map
//        Map<String, Map<File, String>> filesMap = FunnyFileUtils.getFilesMap(codePath);
//        // 获取所有的java文件和class文件
//        Map<File, String> classFiles = filesMap.get("classFiles");
//
//        // 先将项目解析成区域性图
//        classFiles.forEach((file, applicationName) -> {
//            try {
//                ClassReader cr = new ClassReader(new FileInputStream(file));
//                ClassNodeAdapter cna = new ClassNodeAdapter();
//                cr.accept(cna, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
//                parseSingleFileToNode(cna, applicationName);
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        });
//
//        // 解析各方法及类之间得关系
//        classFiles.forEach((file, applicationName) -> {
//            try {
//                ClassReader cr = new ClassReader(new FileInputStream(file));
//                ClassNodeAdapter cna = new ClassNodeAdapter();
//                cr.accept(cna, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
//                parseMultipleFileToNode(cna, applicationName);
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        });
//    }

    // 项目整体解析
    @Override
    public void parseProjectToNode(String codePath) throws FunnyFileNotFoundException{
        deleteAllNodesAndRelations();
        logger.info("已删除所有的Neo4J数据库节点及关系！");
        // 区分gradle项目和maven项目
        List<String> modulePaths = FunnyFileUtils.findModulePaths(codePath, build.getBuildTool());

        logger.info("====================开始将所有Module解析为区域性图！====================");
        for (String modulePath : modulePaths){
            String moduleName = modulePath.substring(modulePath.lastIndexOf(EasyCode.URL_SPLIT)+1);
            logger.info(String.format("【%s】模块开始解析···", moduleName));
            String classesFilePath = FunnyStringUtils.getClassesFilePath(build.getClassesDir());
            String classesPath = FunnyStringUtils.joinPath(modulePath, classesFilePath);
            String applicationNameSrc = FunnyFileUtils.getApplicationName(classesPath);
            String applicationName = applicationNameSrc != null ? applicationNameSrc : moduleName;

            FileFilter classFileFilter = new FileFilter() {
                @Override
                public boolean accept(File file) {
                    return file.isFile() && file.getName().endsWith(".class");
                }
            };
            List<File> classFiles = FunnyFileUtils.listFiles(new File(classesPath), classFileFilter);
            // 先将项目解析成区域性图
            for (File classFile : classFiles){
                try {
                    ClassReader cr = new ClassReader(new FileInputStream(classFile));
                    ClassNodeAdapter cna = new ClassNodeAdapter();
                    cr.accept(cna, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
                    parseSingleFileToNode(cna, applicationName);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            logger.info(String.format("【%s】模块解析完成！", moduleName));
        }
        logger.info("====================将所有Module解析为区域性图结束！====================");

        logger.info("====================开始将所有Module解析为关系性图！====================");
        for (String modulePath : modulePaths){
            String moduleName = modulePath.substring(modulePath.lastIndexOf(EasyCode.URL_SPLIT)+1);
            logger.info(String.format("【%s】模块开始解析···", moduleName));
            String classesFilePath = FunnyStringUtils.getClassesFilePath(build.getClassesDir());
            String classesPath = FunnyStringUtils.joinPath(modulePath, classesFilePath);
            String applicationNameSrc = FunnyFileUtils.getApplicationName(classesPath);
            String applicationName = applicationNameSrc != null ? applicationNameSrc : moduleName;

            FileFilter classFileFilter = new FileFilter() {
                @Override
                public boolean accept(File file) {
                    return file.isFile() && file.getName().endsWith(".class");
                }
            };
            List<File> classFiles = FunnyFileUtils.listFiles(new File(classesPath), classFileFilter);
            // 再将项目解析成关系图
            for (File classFile : classFiles){
                try {
                    ClassReader cr = new ClassReader(new FileInputStream(classFile));
                    ClassNodeAdapter cna = new ClassNodeAdapter();
                    cr.accept(cna, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
                    parseMultipleFileToNode(cna, applicationName);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            logger.info(String.format("【%s】模块解析完成！", moduleName));
        }
        logger.info("====================将所有Module解析为关系性图结束！====================");
    }

    @Override
    public TClassNode save(TClassNode node) {
        return repository.save(node);
    }

    @Override
    public void deleteAllNodesAndRelations() {
        repository.deleteAllNodesAndRelations();
    }

    @Override
    public TClassNode findByName(String name) {
        return repository.findByName(name);
    }

    @Override
    public TClassNode findByNameAndApplicationName(String name, String applicationName) {
        return repository.findByNameAndApplicationName(name, applicationName);
    }
}
